package com.open.system.dispatch.impl;

import com.open.base.dispatch.IGeneralConfigDispatch;
import com.open.base.utils.UserHolderUtils;
import com.open.base.model.LoginUser;
import com.open.base.model.Menu;
import com.open.base.model.Role;
import com.open.base.model.User;
import com.open.base.service.TokenService;
import com.open.common.constants.Constants;
import com.open.common.utils.*;
import com.open.common.enums.ConfigEnum;
import com.open.system.dispatch.*;
import com.open.system.model.Log;
import com.open.system.model.dto.LoginDto;
import com.open.system.model.dto.RegisterDto;
import com.open.system.model.vo.RouterVo;
import com.open.system.service.IUserService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.FastByteArrayOutputStream;

import javax.imageio.ImageIO;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.open.base.utils.UserHolderUtils.getToken;
import static com.open.base.service.TokenService.MILLIS_MINUTE;
import static com.open.common.constants.Constants.CODE_REDIS;
import static com.open.common.constants.Constants.USER_REDIS;
import static com.open.common.constants.TokenConstants.*;
import static com.open.common.enums.LogEnum.LoginType;

/**
 * @author dashu
 * @date 2025/2/2 0:21
 * @description
 */
@Component(value = "loginDispatch")
public class LoginDispatchImpl implements ILoginDispatch {

    // redis令牌有效期（默认60分钟）
    @Value("${token.expireTime}")
    public int expireTime;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private IUserService userService;

    @Autowired
    private IRoleDispatch roleDispatch;

    @Autowired
    private IMenuDispatch menuDispatch;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private ILogDispatch logDispatch;

    @Autowired
    private IGeneralConfigDispatch configDispatch;

    @Override
    public String login(LoginDto loginDto) {
        String config = configDispatch.getSystemConfig(ConfigEnum.CheckCode.getType());
        if (Constants.TRUE.equals(config)) {
            // 验证码校验
            validateCaptcha(loginDto.getUuid(), loginDto.getCheckcode());
        }
        User user = matches(loginDto);
        String token = IdUtils.getUUID();
        //缓存数据
        this.redisCache(user.getId(), token);
        //登录信息填充
        this.recordLoginInfo(user.getId());
        //记录日志
        this.recordLog(user.getId(), user.getName());
        return tokenService.createToken(token);
    }

    @Override
    public Map<String, Object> checkCode() {
        ImageCodeUtils vCode = new ImageCodeUtils(129, 40, 4, 10);

        String code = vCode.getCode();
        String uuid = IdUtils.getUUID();
        redisUtils.setCacheObject(CODE_REDIS + uuid, code, CODE_REDIS_OVERTIME, TimeUnit.SECONDS);

        // 转换流信息写出
        FastByteArrayOutputStream os = new FastByteArrayOutputStream();
        try {
            ImageIO.write(vCode.getBuffImg(), "jpg", os);
        } catch (IOException e) {
            ThrowUtils.throwIf(e.getMessage());
        }

        Map<String, Object> map = new HashMap<>();
        map.put("uuid", uuid);
        map.put("img", Base64Utils.encode(os.toByteArray()));
        return map;
    }

    @Override
    public Map<String, Object> info() {
        //更新缓存
        LoginUser loginUser = this.redisCache(UserHolderUtils.getUserId(), getToken());
        Map<String, Object> map = new HashMap<>();
        map.put("user", loginUser.getUser());
        map.put("roles", loginUser.getRoles());
        map.put("role", loginUser.getRole());
        map.put("permissions", loginUser.getPermissions());
        map.put("menus", new ArrayList<>());
        if (StringUtils.isNotNull(loginUser.getRole())) {
            map.put("menus", menuDispatch.findRouter(loginUser.getRole(), null));
        }
        return map;
    }

    @Override
    public List<RouterVo> getRouters(String menuName) {
        return menuDispatch.findRouter(UserHolderUtils.getRole(), menuName);
    }

    @Override
    public Boolean register(RegisterDto registerDto) {
        String register = configDispatch.getSystemConfig(ConfigEnum.Register.getType());
        ThrowUtils.throwIf(!Constants.TRUE.equals(register), "注册功能暂未开放");
        String config = configDispatch.getSystemConfig(ConfigEnum.CheckCode.getType());
        if (Constants.TRUE.equals(config)) {
            // 验证码校验
            validateCaptcha(registerDto.getUuid(), registerDto.getCheckcode());
        }
        User user = new User();
        BeanUtils.copyProperties(registerDto, user);
        user.setStatus(0);
        user.setPassword(EncryptionUtil.encode(registerDto.getPassword()));
        ThrowUtils.throwIf(!userService.save(user), "用户已存在");
        return true;
    }

    @Override
    public Boolean logout() {
        if (StringUtils.isNotNull(UserHolderUtils.getToken())) {
            redisUtils.deleteObject(USER_REDIS + UserHolderUtils.getToken());
        }
        return true;
    }

    private void validateCaptcha(String uuid, String checkCode) {
        String cacheCode = redisUtils.getCacheObject(CODE_REDIS + uuid);
        ThrowUtils.throwIf(StringUtils.isEmpty(cacheCode), "验证码过期，请重新输入");
        ThrowUtils.throwIf(!cacheCode.equalsIgnoreCase(checkCode), "验证码错误");
    }

    private User matches(LoginDto loginDto) {
        ThrowUtils.throwIf(StringUtils.isBlank(loginDto.getUsername()) || StringUtils.isBlank(loginDto.getPassword()), "用户名和密码都不能为空!");
        //对密码进行加密
        User user = new User();
        user.setUsername(loginDto.getUsername());
        user.setPassword(EncryptionUtil.encode(loginDto.getPassword()));
        User one = CollectionUtils.isNotEmpty(userService.findByModel(user)) ? userService.findByModel(user).get(0) : null;
        ThrowUtils.throwIf(one == null, "用户名或密码错误");
        ThrowUtils.throwIf(one != null && one.getStatus() == 1, "用户已被禁用");
        return one;
    }

    /**
     * 记录登录信息
     *
     * @param userId 用户ID
     */
    public void recordLoginInfo(Long userId) {
        User user = new User();
        user.setId(userId);
        user.setLoginIp(IpUtils.getIpAddr());
        user.setLoginDate(DateUtils.getCurrentTimeAsString());
        userService.update(user);
    }

    //缓存并返回
    private LoginUser redisCache(Long userId, String token) {
        User user = userService.findByPk(userId);
        List<Role> roles = roleDispatch.findByUserId(userId);
        Long roleId = roleDispatch.getRoleId(userId);
        Role role = roleDispatch.findByPk(roleId);
        //权限字符串
        List<Menu> permissions = menuDispatch.findMenuByRoleId(roleId);
        //超级管理员拥有所有权限
        if (role != null && role.getStatus() == 0 && Role.isAdmin(role.getRoleKey())) {
            permissions = menuDispatch.findAllMenu();
        }
        LoginUser loginUser = new LoginUser(token, DateUtils.getCurrentTimes(), DateUtils.getCurrentTimes() + tokenService.expireTime * MILLIS_MINUTE, role, permissions, roles, user);
        redisUtils.setCacheObject(USER_REDIS + token, loginUser, expireTime, TimeUnit.MINUTES);
        return loginUser;
    }

    //记录日志
    public void recordLog(Long id, String name) {
        String content = "用户：" + name + " 登录系统";
        logDispatch.save(new Log(DateUtils.getCurrentTimeAsString(), id, Constants.ROLE_KEY_COMMON, LoginType.getStatus(), LoginType.getTitle(), content, 1));
    }
}
